// 使用管道

#include <iostream>
#include <cassert>
#include <cerrno>
#include <string>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;

/*
* 四种管道场景
* 一、读端读取完所有数据，只能等待写端写入。
*    实现：读端睡眠一段时间
*    现象：读端睡眠时，写端不断写入数据，导致读端一次读出多份数据
*二、写端将管道写满，无法再写入
*    实现：读端暂停或延缓读取，写端不断写入，一次写入1字节
*    结果：写端至多写入 65535 字节的数据，即 16 * 4096 -> 64kb 大小的数据（16个数据块）
*三、关闭写端的fd，读端会发生什么现象
*    实现：写端写入一次数据后，终止写入，即关闭写端fd
*    现象：读端成功读取一次后，read 函数返回 0，表示读至文件结尾（写端已关闭，无法再读取）
*四、关闭读端fd，观察现象
*    实现：读取n次后，关闭读端fd
*    现象：读端关闭后，OS认为写端没有必要存在，发出13号信号将其终止
*/


int main()
{
    // 1、创建管道
    int pipefd[2] = {0};
    int ret = pipe(pipefd); // 打开管道
    if (ret == -1)
    {
        cout << "pipe fail! " << strerror(errno) << endl;
        return 1;
    }

    cout << "pipefd[0]: " << pipefd[0] << endl;
    cout << "pipefd[0]: " << pipefd[1] << endl;

    // 2、创建子进程
    pid_t id = fork();
    assert(id != -1);
    (void)id;

    // 此时规定：父进程读取、子进程写入

    if (id == 0)
    {
        // 子进程内
        // 3、关闭不需要的 fd
        close(pipefd[0]);      // 关闭读取，只写入

        //4、开始通信
        string str = "Hello! 我是子进程";
        int cnt = 1;
        char buff[1024] = "0"; // 缓冲区

        while (true)
        {
            snprintf(buff, sizeof buff, "%s %d 我的 PID:%d\n", str.c_str(), cnt, getpid());
            write(pipefd[1], buff, strlen(buff));
            cnt++;
            //sleep(3);   //场景三
            //break;  //场景三
            sleep(1);
        }

        // //场景二
        // int cnt = 0;
        // while(true)
        // {
        //     char x = 'X';
        //     write(pipefd[1], &x, 1);
        //     cout << "cnt: " << cnt << endl;
        //     cnt++;
        // }

        close(pipefd[1]); // 写也要关闭
        exit(-1);         // 子进程终止
    }

    // 3、关闭不需要的 fd
    close(pipefd[1]); // 关闭写，只读

    // 4、开始通信
    char buff[1024] = "0";
    int cnt = 0;
    while (true)
    {
        //sleep(10); //场景一
        //sleep(100); //场景二，父进程不读，等写端不断写入
        int n = read(pipefd[0], buff, sizeof(buff) - 1);
        if(n > 0)
        {
            buff[n] = '\0';
            cout << "chile give me message: " << buff << endl;
        }
        else if(n == 0)
            cout << "Read for end" << endl;
        sleep(1);   //场景四
        cout << "cnt: " << cnt << endl;
        if(cnt == 5)
            break;
        cnt++;
    }

    close(pipefd[0]); // 读也要关闭

    int status = 0;
    waitpid(id, &status, 0);
    cout << "sign: " << (status & 0x7f) << endl;

    return 0;
}